#include "scenenode.h"
#include "meshscenenode.h"
#include <string.h>
#include <rmxfguid.h>
#include "game.h"

/**
 * Konstruktor
 */
SceneNode::SceneNode(SceneNode* parentNode, std::string &name)
	: parentNode(parentNode), name(name)
{
	localTransformation = Matrix::identity();
	absoluteTransformation = Matrix::identity();
	boundingBox = BoundingBox();
	flag = getFlagFromName(name);
}

/**
 * Destruktor
 */
SceneNode::~SceneNode()
{
	subNodes.clear();
}

/**
 * Zraca flage wezla na podstawie jego nazwy
 */
SceneNode::Flag SceneNode::getFlagFromName(const std::string &name)
{
	std::string flagPart;
	size_t flagPartPos = name.find('_');
	if(flagPartPos == std::string::npos)
		flagPart = name;
	else
		flagPart = name.substr(0, flagPartPos);

	if(0==_stricmp(flagPart.c_str(), "SpawnPoint"))
		return SceneNode::SpawnPoint;
	else if(0==_stricmp(flagPart.c_str(), "Finish"))
		return SceneNode::Finish;
	else if(0==_stricmp(flagPart.c_str(), "NoClip"))
		return SceneNode::NoClip;
	else if(0==_stricmp(flagPart.c_str(), "Kill"))
		return SceneNode::Kill;
	else if(0==_stricmp(flagPart.c_str(), "SkyBox"))
		return SceneNode::SkyBox;
	else if(0==_stricmp(flagPart.c_str(), "Player"))
		return SceneNode::Player;
	else
		return SceneNode::Normal;
}

/**
 * Renderuje wezel sceny
 */
void SceneNode::draw()
{
	// renderuj tylko wezly normalne, noclip, skybox-a i playera
	if((flag != Normal) && (flag != NoClip) && (flag != SkyBox) && (flag != Player))
		return;

	// renderuj podwezly
	std::list<SceneNode*>::const_iterator it;
	for(it = subNodes.begin(); it != subNodes.end(); it++)
		(*it)->draw();
}


/**
 * Odswierza macierz bezwzglednej transformacji
 */
void SceneNode::refreshAbsoluteTransformation(bool recursive)
{
	if(parentNode)
		absoluteTransformation =
			parentNode->getAbsoluteTransformation() * localTransformation;
	else
		absoluteTransformation = localTransformation;
	if(recursive)
	{
		boundingBox = getAbsoluteTransformation() * BoundingBox();
		std::list<SceneNode*>::iterator it;
		for(it = subNodes.begin(); it != subNodes.end(); it++)
		{
			(*it)->refreshAbsoluteTransformation(true);
			boundingBox.addInternalBox((*it)->getBoundingBox());
		}
	}
}

/**
 * Laduje wezel sceny i podwezly z danych pliku X.
 */
bool SceneNode::loadFromXof(LPD3DXFILEDATA xofFileData)
{
	char *c_name;
	SIZE_T nameSize PURE;
	SIZE_T childCount;
	GUID subnodeType;
	LPD3DXFILEDATA xofChildData;

	// domyslna macierz lokalnej transformacji
	localTransformation = Matrix::identity();

	// wczytaj nazwe wezla
	if(FAILED(xofFileData->GetName(NULL, &nameSize)))
		return false;
	if(!(c_name = new char[nameSize+1]))
		return false;
	if(FAILED(xofFileData->GetName(c_name, &nameSize)))
		return false;
	c_name[nameSize] = 0;
	name = c_name;
	delete[] c_name;

	// ustaw flage na podstawie nazwy
	flag = getFlagFromName(name);

	// liczba podwezlow
	if(FAILED(xofFileData->GetChildren(&childCount)))
		return false;

	// wczytaj podwezly
	for(SIZE_T child = 0; child < childCount; child++)
	{
		// wczytaj dane podwezla
		if(FAILED(xofFileData->GetChild(child, &xofChildData)))
			return false;

		// wczytaj typ podwezla
		if(FAILED(xofChildData->GetType(&subnodeType)))
			return false;

		// typ podwezla == macierz transformacji ramki?
		if(TID_D3DRMFrameTransformMatrix == subnodeType)
		{
				SIZE_T nodeDataSize = sizeof(Matrix);
				LPCVOID nodeData = NULL;

				if(FAILED(xofChildData->Lock(&nodeDataSize, &nodeData)))
					return false;
				localTransformation = Matrix((const float*)nodeData);
				if(FAILED(xofChildData->Unlock()))
					return false;
		}
		// typ podwezla == ramka?
		else if(TID_D3DRMFrame == subnodeType)
		{
			// dodaj nowy podwezel
			SceneNode* subNode = new SceneNode(this);

			// wczytaj go
			if(subNode->loadFromXof(xofChildData))
				subNodes.push_back(subNode);
			else
				delete subNode;
		}
		// typ podwezla == siatka?
		else if(TID_D3DRMMesh == subnodeType)
		{
			// dodaj nowy podwezel siatki
			MeshSceneNode* subNode = new MeshSceneNode(this, name + "_mesh");

			// wczytaj go
			if(subNode->loadFromXof(xofChildData))
				subNodes.push_back(subNode);
			else
				delete subNode;
		}
	}

	// koniec
	return true;
}

/**
 * Znajduje podwezel na podstawie flagi
 */
SceneNode* SceneNode::getSubNodeFromFlag(SceneNode::Flag f) const
{
	std::list<SceneNode*>::const_iterator it;
	for(it = subNodes.begin(); it != subNodes.end(); it++)
	{
		SceneNode* n = NULL;
		if((*it)->getFlag() == f)
			return (*it);
		else if(n = ((*it)->getSubNodeFromFlag(f)))
			return n;
	}
	return NULL;
}

/**
 * Sprawdza kolizje
 */
void SceneNode::checkCollision(const BoundingBox &playerBox,
	float playerRadius, Physics::CollisionPacket &col) const
{
	if((flag == NoClip) || (flag == SkyBox) || (flag == Player))
		return;

	BoundingBox box = boundingBox;
	if(box.isCollidingWithBox(playerBox))
	{
	
		//Game::getGraphics()->drawWireframeBox(box.getMinPoint(), box.getMaxPoint());

		std::list<SceneNode*>::const_iterator it;
		for(it = subNodes.begin(); it != subNodes.end(); it++)
			(*it)->checkCollision(playerBox, playerRadius, col);
	}
}
